/**
 ******************************************************************************
 * @file           : bsp_spi.c
 * @brief          : SPI Driver Implementation (STM32H7xx)
 ******************************************************************************
 * @attention
 *
 * SPI驱动程序实现文件
 * 提供SPI主机模式通信功能
 * 支持阻塞式收发和完整的错误处理
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/SPI/bsp_spi.h"
#include "./BSP/SPI/bsp_spi_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define SPI_TX_TIMEOUT          1000        /*!< 发送超时时间(ms) */
#define SPI_RX_TIMEOUT          1000        /*!< 接收超时时间(ms) */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static SPI_HandleTypeDef sg_spi_handle;                      /*!< SPI句柄 */
static volatile SPI_State_e sg_spi_state = SPI_STATE_IDLE;  /*!< SPI当前状态 */
static volatile uint8_t sg_spi_initialized = 0;             /*!< 初始化标志 */

/* Private function prototypes -----------------------------------------------*/
static void spi_gpio_init(void);
static void spi_peripheral_init(void);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       SPI GPIO初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SCK、MISO、MOSI、CS引脚
 */
static void spi_gpio_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    
    /* 使能GPIO时钟 */
    SPI_SCK_GPIO_CLK_ENABLE();
    SPI_MOSI_GPIO_CLK_ENABLE();
    SPI_MISO_GPIO_CLK_ENABLE();
    SPI_CS_GPIO_CLK_ENABLE();
    
    /* 配置SCK引脚 - 复用推挨输出 */
    gpio_init_struct.Pin = SPI_SCK_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_struct.Alternate = SPI_GPIO_AF;
    HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &gpio_init_struct);
    
    /* 配置MISO引脚 - 复用输入 */
    gpio_init_struct.Pin = SPI_MISO_GPIO_PIN;
    HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &gpio_init_struct);
    
    /* 配置MOSI引脚 - 复用推挨输出 */
    gpio_init_struct.Pin = SPI_MOSI_GPIO_PIN;
    HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &gpio_init_struct);
    
    /* 配置CS引脚 - 通用推挨输出 */
    gpio_init_struct.Pin = SPI_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_struct.Alternate = 0;  /* 不是复用功能 */
    HAL_GPIO_Init(SPI_CS_GPIO_PORT, &gpio_init_struct);
    
    /* CS默认拉高（未选中） */
    HAL_GPIO_WritePin(SPI_CS_GPIO_PORT, SPI_CS_GPIO_PIN, GPIO_PIN_SET);
}

/**
 * @brief       SPI外设初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SPI工作模式和参数
 */
static void spi_peripheral_init(void)
{
    /* 使能SPI时钟 */
    SPI_SPIx_CLK_ENABLE();
    
    /* 配置SPI参数 */
    sg_spi_handle.Instance = SPI_SPIx;
    sg_spi_handle.Init.Mode = SPI_MODE_MASTER;                      /* 主机模式 */
    sg_spi_handle.Init.Direction = SPI_DIRECTION_2LINES;            /* 双线模式 */
    sg_spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;                /* 8位数据 */
    sg_spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;              /* 时钟极性 */
    sg_spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;                  /* 时钟相位 */
    sg_spi_handle.Init.NSS = SPI_NSS_SOFT;                          /* 软件控制CS */
    sg_spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 波特率分频 */
    sg_spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;                 /* 高位优先 */
    sg_spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;                 /* 禁用TI模式 */
    sg_spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁用CRC */
    
    if (HAL_SPI_Init(&sg_spi_handle) != HAL_OK)
    {
        log_e("SPI外设初始化失败");
        return;
    }
    
    /* 使能SPI */
    __HAL_SPI_ENABLE(&sg_spi_handle);
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       SPI初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        初始化GPIO、SPI外设和片选
 */
void spi_init(void)
{
    /* 初始化GPIO */
    spi_gpio_init();
    
    /* 初始化SPI外设 */
    spi_peripheral_init();
    
    /* 设置状态 */
    sg_spi_state = SPI_STATE_IDLE;
    sg_spi_initialized = 1;
    
    log_i("SPI初始化完成");
}

/**
 * @brief       SPI反初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        关闭SPI外设和释放GPIO资源
 */
void spi_deinit(void)
{
    if (!sg_spi_initialized)
    {
        return;
    }
    
    /* 禁用SPI */
    __HAL_SPI_DISABLE(&sg_spi_handle);
    
    /* 反初始化SPI */
    HAL_SPI_DeInit(&sg_spi_handle);
    
    /* 释放GPIO */
    HAL_GPIO_DeInit(SPI_SCK_GPIO_PORT, SPI_SCK_GPIO_PIN);
    HAL_GPIO_DeInit(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN);
    HAL_GPIO_DeInit(SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PIN);
    HAL_GPIO_DeInit(SPI_CS_GPIO_PORT, SPI_CS_GPIO_PIN);
    
    /* 清除标志 */
    sg_spi_initialized = 0;
    sg_spi_state = SPI_STATE_IDLE;
    
    log_i("SPI已反初始化");
}

/**
 * @brief       选中从机
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        拉低CS信号
 */
void spi_cs_select(void)
{
    HAL_GPIO_WritePin(SPI_CS_GPIO_PORT, SPI_CS_GPIO_PIN, GPIO_PIN_RESET);
}

/**
 * @brief       取消选中
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        拉高CS信号
 */
void spi_cs_deselect(void)
{
    HAL_GPIO_WritePin(SPI_CS_GPIO_PORT, SPI_CS_GPIO_PIN, GPIO_PIN_SET);
}

/**
 * @brief       SPI读写单字节
 *
 * @param[in]   tx_data 要发送的字节
 *
 * @retval      读取的字节
 *
 * @note        全双工收发，同时发送和接收一个字节
 */
uint8_t spi_read_write_byte(uint8_t tx_data)
{
    uint8_t rx_data;
    HAL_SPI_TransmitReceive(&sg_spi_handle, &tx_data, &rx_data, 1, SPI_TX_TIMEOUT);
    return rx_data;
}

/**
 * @brief       SPI写多字节
 *
 * @param[in]   data 数据指针
 * @param[in]   len  数据长度(字节)
 *
 * @retval      SPI_OK         写成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_TIMEOUT 写超时
 *
 * @note        阻塞写操作
 */
SPI_Error_e spi_write(const uint8_t *data, uint16_t len)
{
    HAL_StatusTypeDef status;
    
    if (data == NULL || len == 0)
    {
        log_w("SPI写参数错误: data=%p, len=%d", data, len);
        return SPI_ERROR_PARAM;
    }
    
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }
    
    sg_spi_state = SPI_STATE_TX;
    status = HAL_SPI_Transmit(&sg_spi_handle, (uint8_t*)data, len, SPI_TX_TIMEOUT);
    sg_spi_state = SPI_STATE_IDLE;
    
    if (status != HAL_OK)
    {
        log_e("SPI写失败: status=%d, len=%d", status, len);
        return SPI_ERROR_TIMEOUT;
    }
    
    log_d("SPI写成功: %d字节", len);
    return SPI_OK;
}

/**
 * @brief       SPI读多字节
 *
 * @param[out]  data 数据缓冲区指针
 * @param[in]   len  数据长度(字节)
 *
 * @retval      SPI_OK         读成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_TIMEOUT 读超时
 *
 * @note        阻塞读操作
 */
SPI_Error_e spi_read(uint8_t *data, uint16_t len)
{
    HAL_StatusTypeDef status;
    
    if (data == NULL || len == 0)
    {
        log_w("SPI读参数错误: data=%p, len=%d", data, len);
        return SPI_ERROR_PARAM;
    }
    
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }
    
    sg_spi_state = SPI_STATE_RX;
    status = HAL_SPI_Receive(&sg_spi_handle, data, len, SPI_RX_TIMEOUT);
    sg_spi_state = SPI_STATE_IDLE;
    
    if (status != HAL_OK)
    {
        log_e("SPI读失败: status=%d, len=%d", status, len);
        return SPI_ERROR_TIMEOUT;
    }
    
    log_d("SPI读成功: %d字节", len);
    return SPI_OK;
}

/**
 * @brief       SPI全双工收发
 *
 * @param[in]   tx_data 发送数据指针
 * @param[out]  rx_data 接收数据指针
 * @param[in]   len     数据长度(字节)
 *
 * @retval      SPI_OK         收发成功
 * @retval      SPI_ERROR_PARAM 参数错误
 * @retval      SPI_ERROR_TIMEOUT 收发超时
 *
 * @note        阻塞全双工收发操作
 */
SPI_Error_e spi_write_read(const uint8_t *tx_data, uint8_t *rx_data, uint16_t len)
{
    HAL_StatusTypeDef status;
    
    if (tx_data == NULL || rx_data == NULL || len == 0)
    {
        log_w("SPI收发参数错误: tx=%p, rx=%p, len=%d", tx_data, rx_data, len);
        return SPI_ERROR_PARAM;
    }
    
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }
    
    sg_spi_state = SPI_STATE_TX_RX;
    status = HAL_SPI_TransmitReceive(&sg_spi_handle, (uint8_t*)tx_data, rx_data, len, SPI_TX_TIMEOUT);
    sg_spi_state = SPI_STATE_IDLE;
    
    if (status != HAL_OK)
    {
        log_e("SPI收发失败: status=%d, len=%d", status, len);
        return SPI_ERROR_TIMEOUT;
    }
    
    log_d("SPI收发成功: %d字节", len);
    return SPI_OK;
}

/**
 * @brief       获取SPI当前状态
 *
 * @param       无
 *
 * @retval      SPI_State_e 当前状态
 *
 * @note        用于状态监控和调试
 */
SPI_State_e spi_get_state(void)
{
    return sg_spi_state;
}

/**
 * @brief       检查SPI是否忙
 *
 * @param       无
 *
 * @retval      1 忙
 * @retval      0 空闲
 *
 * @note        快速检查SPI是否正在传输
 */
uint8_t spi_is_busy(void)
{
    return (sg_spi_state != SPI_STATE_IDLE) ? 1 : 0;
}

/**
 * @brief       打印SPI当前状态（用于调试）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        显示初始化状态、当前状态等信息
 */
void spi_print_status(void)
{
    const char *state_str[] = {"IDLE", "BUSY", "TX", "RX", "TX_RX", "ERROR"};
    
    log_i("SPI当前状态:");
    log_i("  初始化状态: %s", sg_spi_initialized ? "已初始化" : "未初始化");
    log_i("  当前状态: %s", state_str[sg_spi_state]);
    log_i("  SPI是否忙: %s", spi_is_busy() ? "是" : "否");
}
